#include "LibXml2XmlImpl.h"

#define SAFE_DELETE(o) if(o) delete o; o = NULL;

XML_TOOL_NAMESPACE_BEGIN

LibXml2XmlNode::LibXml2XmlNode(const LibXml2XmlDocument* doc, LibXml2XmlNode* parent, xmlNodePtr node)
: mDoc(doc)
{
	mNode = node;
	mParent = parent;
	mFirstChild = NULL;
	mLastChild = NULL;
	mNextSibling = NULL;
	mPrevSibling = NULL;
	xmlNodePtr child = node->children;
	if(!child)
		return;

	LibXml2XmlNode* current = new LibXml2XmlNode(doc, this, child);
	mFirstChild = current;
	child = child->next;
	while(child)
	{
		mLastChild = new LibXml2XmlNode(doc, this, child);
		current->mNextSibling = mLastChild;
		mLastChild->mPrevSibling = current;
		current = mLastChild;
		child = child->next;
	}
}

LibXml2XmlNode::~LibXml2XmlNode()
{
	Release();
}

bool LibXml2XmlNode::GetAttribute(const string& attrName, string& attrValue) const
{
	xmlChar* szValue = xmlGetProp(mNode, (xmlChar*)attrName.c_str());
	if(szValue == NULL)
		return false;
	attrValue = (char*)szValue;
	xmlFree(szValue);
	return true;
}

void LibXml2XmlNode::SetAttribute(const string& attrName, const string& attrValue)
{
	xmlNewProp(mNode, (xmlChar*)attrName.c_str(), (xmlChar*)attrValue.c_str());
}

XmlNodePtr LibXml2XmlNode::GetParent() const
{
	return mParent;
}

XmlNodePtr LibXml2XmlNode::GetFirstChild() const
{
	return mFirstChild;
}

XmlNodePtr LibXml2XmlNode::GetLastChild() const
{
	return mLastChild;
}

XmlNodePtr LibXml2XmlNode::GetNextSibling() const
{
	return mNextSibling;
}

XmlNodePtr LibXml2XmlNode::GetPrevSibling() const 
{
	return mPrevSibling;
}

const XmlDocumentPtr LibXml2XmlNode::GetDocument() const
{
	return const_cast<LibXml2XmlDocument*>(mDoc);
}

string LibXml2XmlNode::GetNodeName() const
{
	return mNode->name ? (char*)mNode->name : "";
}

string LibXml2XmlNode::GetTextContent() const
{
	LibXml2XmlNode* childNode = mFirstChild;
	while(childNode)
	{
		if(childNode->GetNodeType() == XML_TEXT_NODE)
			return (char*)childNode->mNode->content;
	}
	return "";
}

XmlNodeType LibXml2XmlNode::GetNodeType() const
{
	return (XmlNodeType)mNode->type;
}

void LibXml2XmlNode::AppendChild(XmlNodePtr childNode)
{
	LibXml2XmlNode* xmlNode = (LibXml2XmlNode*)childNode;
	xmlAddChild(mNode, xmlNode->mNode);
	xmlNode->mParent = this;
	if(mLastChild)
		xmlNode->mPrevSibling = mLastChild;
	else
		xmlNode->mPrevSibling = mFirstChild;
	if(mLastChild)
		mLastChild->mNextSibling = xmlNode;
	if(mFirstChild == NULL)
		mFirstChild = xmlNode;
	mLastChild = xmlNode;
}

string LibXml2XmlNode::BindToXmlText(const string& encoding)
{
	xmlBufferPtr buffer = xmlBufferCreate();
	xmlNodeDump(buffer, mDoc->mDoc, mNode, 0, 0);
	string xmlText = (char*)buffer->content;
	xmlBufferFree(buffer);
	return xmlText;
}

void LibXml2XmlNode::Release()
{
	LibXml2XmlNode* xmlNode = mFirstChild;
	while(xmlNode)
	{
		LibXml2XmlNode* next = xmlNode->mNextSibling;
		SAFE_DELETE(xmlNode);
		xmlNode = next;
	}
	mFirstChild = NULL;
	mLastChild = NULL;
	mNextSibling = NULL;
	mPrevSibling = NULL;
	mParent = NULL;
}

LibXml2XmlDocument::LibXml2XmlDocument(xmlDocPtr doc)
{
	mDoc = doc;
	mRootNode = NULL;
	xmlNodePtr root = xmlDocGetRootElement(mDoc);
	if(root != NULL)
		mRootNode = new LibXml2XmlNode(this, NULL, root);
}

LibXml2XmlDocument::~LibXml2XmlDocument()
{
	ReleaseXmlNode(mRootNode);
	xmlFreeDoc(mDoc);
}

XmlNodePtr LibXml2XmlDocument::GetDocumentRoot() const
{
	return mRootNode;
}

void LibXml2XmlDocument::SetDocumentRoot(XmlNodePtr xmlNode)
{
	SAFE_DELETE(mRootNode);
	mRootNode = (LibXml2XmlNode*)xmlNode;
	xmlDocSetRootElement(mDoc, mRootNode->mNode);
}

XmlNodePtr LibXml2XmlDocument::CreateXmlNode(const string& nodeName)
{
	xmlNodePtr xmlNode = xmlNewNode(NULL, (xmlChar*)nodeName.c_str());
	return new LibXml2XmlNode(this, NULL, xmlNode);
}

XmlNodePtr LibXml2XmlDocument::CreateTextNode(const string& nodeName, const string& nodeValue)
{
	XmlNodePtr node = CreateXmlNode(nodeName);
	LibXml2XmlNode* text = new LibXml2XmlNode(this, NULL, xmlNewText((xmlChar*)nodeValue.c_str()));
	node->AppendChild(text);
	return node;
}

string LibXml2XmlDocument::BindToXmlText(const string& encoding)
{
	xmlChar* szValue = NULL;
	int nSize = 0;
	xmlDocDumpMemoryEnc(mDoc, &szValue, &nSize, encoding.c_str());
	if(szValue)
	{
		string xml = (char*)szValue;
		xmlFree(szValue);
		return xml;
	}
	return "";
}

LibXml2XmlDocument* LibXml2XmlDocument::CreateDocument()
{
	return new LibXml2XmlDocument(xmlNewDoc((xmlChar*)"1.0"));
}

LibXml2XmlDocument* LibXml2XmlDocument::CreateDocument(const string& xmlText, const string& encoding)
{
	xmlDocPtr xmlDoc = xmlReadMemory(xmlText.c_str(), xmlText.length(), NULL, NULL, 0);
	return new LibXml2XmlDocument(xmlDoc);
}

void LibXml2XmlDocument::ReleaseXmlNode(XmlNodePtr xmlNode)
{
	delete xmlNode;
}

XML_TOOL_NAMESPACE_END